package com.kinghunt.misp.service.impl.userx;

import com.alibaba.fastjson.JSON;
import com.kinghunt.misp.common.constant.SystemConstant;
import com.kinghunt.misp.common.dto.userx.RoleDTO;
import com.kinghunt.misp.common.dto.userx.UserDTO;
import com.kinghunt.misp.common.dto.userx.UserImportDTO;
import com.kinghunt.misp.common.enums.userx.CertificateTypeEnum;
import com.kinghunt.misp.common.enums.userx.RoleEnum;
import com.kinghunt.misp.common.enums.userx.UserCreateTypeEnum;
import com.kinghunt.misp.common.enums.userx.UserStatusEnum;
import com.kinghunt.misp.common.param.userx.UserParam;
import com.kinghunt.misp.common.param.userx.WeChatUserParam;
import com.kinghunt.misp.common.utils.CacheKeyUtils;
import com.kinghunt.misp.dal.dao.userx.OrganizationDAO;
import com.kinghunt.misp.dal.dao.userx.OrganizationUserDAO;
import com.kinghunt.misp.dal.dao.userx.UserDAO;
import com.kinghunt.misp.dal.dao.userx.UserRoleDAO;
import com.kinghunt.misp.dal.model.userx.OrganizationDO;
import com.kinghunt.misp.dal.model.userx.OrganizationUserDO;
import com.kinghunt.misp.dal.model.userx.UserDO;
import com.kinghunt.misp.dal.model.userx.UserRoleDO;
import com.kinghunt.misp.service.api.userx.UserWriteService;
import com.bygoodtech.kings.base.utils.ConvertUtil;
import com.bygoodtech.kings.common.dto.ApiResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.kinghunt.misp.common.utils.PasswordUtils.passwordEncoder;

/**
 * @Description 用户
 * @Author jiangtao
 * @Date 2023/7/5 22:28
 */
@Slf4j
@Service
public class UserWriteServiceImpl implements UserWriteService {

    @Autowired
    private UserDAO userDao;
    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private OrganizationDAO organizationDAO;

    @Autowired
    private OrganizationUserDAO organizationUserDAO;

    @Autowired
    private UserRoleDAO userRoleDAO;

    @Override
    public ApiResult<Long> addUser(UserParam userParam) {
        UserDO userDO = userDao.getUserByUsernameAndOrgId(userParam.getUsername(), userParam.getOrganizationId());
        if (userDO != null) {
            log.error("userName " + userDO.getUsername() + " : orgId " + userDO.getOrganizationId() + "exists");
            throw new IllegalArgumentException("用户名已存在");
        }

        UserDO newUserDO = ConvertUtil.convert(userParam, UserDO.class);
        newUserDO.setIsDel(Boolean.FALSE);
        newUserDO.setSalt(DigestUtils.md5Hex(UUID.randomUUID().toString() + System.currentTimeMillis() + UUID.randomUUID().toString()));
        newUserDO.setPassword(passwordEncoder(newUserDO.getPassword(), newUserDO.getSalt()));
        newUserDO.setStatus(UserStatusEnum.ACTIVE.getValue());
        newUserDO.setCreateGmt(new Date());
        int result = userDao.addUser(newUserDO);
        Long userId = newUserDO.getId();

        UserRoleDO userRoleDO = new UserRoleDO();
        userRoleDO.setUserId(userId);
        userRoleDO.setRoleId(userParam.getRoleId());
        userRoleDAO.insertSelective(userRoleDO);

        OrganizationUserDO organizationUserDO = new OrganizationUserDO();
        organizationUserDO.setUserId(userId);
        organizationUserDO.setOrganizationId(userParam.getOrganizationId());
        organizationUserDAO.insertSelective(organizationUserDO);

        log.info("addUser success, {}", userParam.getUsername());
        return ApiResult.ok(userId);
    }

    @Override
    public ApiResult<Long> registerUserInWeChat(WeChatUserParam param) {
        OrganizationDO organizationDO = organizationDAO.selectByPrimaryKey(param.getOrganizationId());
        if (Objects.isNull(organizationDO)) {
            log.error("organization {} not exist ", param.getOrganizationId());
            throw new IllegalArgumentException("注册失败，请稍后重试");
        }

        UserDO userDO;
        userDO = userDao.getUserByUsernameAndOrgId(param.getUsername(), param.getOrganizationId());
        if (Objects.nonNull(userDO)) {
            log.error("userName " + userDO.getUsername() + " : orgId " + userDO.getOrganizationId() + "exists");
            throw new IllegalArgumentException("该用户名已注册");
        }

        userDO = userDao.getUserByOpenIdAndOrgId(param.getOpenId(), param.getOrganizationId());
        if (Objects.nonNull(userDO)) {
            log.error("openId " + userDO.getOpenId() + " : orgId " + userDO.getOrganizationId() + "exists");
            throw new IllegalArgumentException("该微信已注册");
        }

        userDO = userDao.getUserByPhoneAndOrgId(param.getPhone(), param.getOrganizationId());
        if (Objects.nonNull(userDO)) {
            // 手机号被注册，但微信openId为空，说明已经通过其他方式注册，本次通过微信更新相关信息。注意：用户名不可在微信端更新
            if (StringUtils.isEmpty(userDO.getOpenId())) {
                UserDO updateUser = new UserDO();
                updateUser.setId(userDO.getId());
                updateUser.setOpenId(param.getOpenId());
                updateUser.setSessionKey(param.getSessionKey());
                updateUser.setAvatarUrl(param.getAvatarUrl());
                updateUser.setNickname(param.getNickname());
                updateUser.setRealname(param.getRealname());
                // 微信小程序不可修改用户名
                updateUser.setUsername(null);
                updateUser.setUpdateGmt(new Date());
                updateUser.setUpdaterId(userDO.getId());
                userDao.updateByPrimaryKeySelective(updateUser);
                return ApiResult.ok(userDO.getId());
            } else {
                // 手机号被另外一个微信openId占用
                log.error("phone " + userDO.getPhone() + " : orgId " + userDO.getOrganizationId() + "exists");
                throw new IllegalArgumentException("该手机号已注册");
            }
        }

        // 以上校验通过，表示该用户未注册过，orgId + username/openId/phone 均为被占用
        UserDO newUserDO = ConvertUtil.convert(param, UserDO.class);
        newUserDO.setAppCode(SystemConstant.SYSTEM_CODE);
        newUserDO.setIsDel(Boolean.FALSE);
        newUserDO.setSalt(DigestUtils.md5Hex(UUID.randomUUID().toString() + System.currentTimeMillis() + UUID.randomUUID().toString()));
        newUserDO.setPassword(passwordEncoder(SystemConstant.DEFAULT_USER_PASSWORD, newUserDO.getSalt()));
        newUserDO.setStatus(UserStatusEnum.ACTIVE.getValue());
        newUserDO.setCreateGmt(new Date());
        newUserDO.setCreateType(UserCreateTypeEnum.WECHAT_REG.getType());
        newUserDO.setOrganizationName(organizationDO.getName());
        userDao.addUser(newUserDO);
        Long userId = newUserDO.getId();

        UserRoleDO userRoleDO = new UserRoleDO();
        userRoleDO.setUserId(userId);
        // TODO : 默认角色
        userRoleDO.setRoleId(RoleEnum.ORG_STU.getId());
        userRoleDAO.insertSelective(userRoleDO);

        OrganizationUserDO organizationUserDO = new OrganizationUserDO();
        organizationUserDO.setUserId(userId);
        organizationUserDO.setOrganizationId(param.getOrganizationId());
        organizationUserDAO.insertSelective(organizationUserDO);

        log.info("addUser success, {}", param.getUsername());
        return ApiResult.ok(userId);
    }

    @Override
    public ApiResult updateUserInfo(UserParam userParam) {
        UserDO newUserDO = ConvertUtil.convert(userParam, UserDO.class);
        newUserDO.setUpdateGmt(new Date());
        int result = userDao.updateByPrimaryKeySelective(newUserDO);

        // 更新角色
        userRoleDAO.deleteByUserId(userParam.getId());
        UserRoleDO userRole = new UserRoleDO();
        userRole.setUserId(userParam.getId());
        userRole.setRoleId(userParam.getRoleId());
        userRoleDAO.insertSelective(userRole);

        // 机构不允许变更

        // TODO: 2019/11/9 update user shiro session
        return ApiResult.ok();
    }

    @Override
    public ApiResult updatePassword(long userId, String oldPassword, String newPassword) {
        List<UserDO> userById = userDao.getUserByIds(Collections.singletonList(userId));
        if (CollectionUtils.isEmpty(userById)) {
            throw new IllegalArgumentException("user not exists");
        }
        if (!userById.get(0).getPassword().equals(passwordEncoder(oldPassword, userById.get(0).getSalt()))) {
            throw new IllegalArgumentException("password error");
        }
        int result = userDao.updatePassword(passwordEncoder(newPassword, userById.get(0).getSalt()), userId);
        log.info("updatePassword success, {}", userId);
        return ApiResult.ok();
    }

    @Override
    public ApiResult resetPassword(UserDTO loginUser, Long userId) {
        log.info("reset password : {} - {}", loginUser.getId(), userId);
        String newPassword = SystemConstant.DEFAULT_USER_PASSWORD;

        List<RoleDTO> roles = loginUser.getRoles();
        if (CollectionUtils.isEmpty(roles)) {
            log.info("reset password fail : {} - {}", loginUser.getId(), userId);
            return ApiResult.failOfMessage("无操作权限");
        }

        UserDO user = userDao.selectByPrimaryKey(userId);
        List<Long> roleIds = roles.stream().map(RoleDTO::getId).collect(Collectors.toList());
        // 超级管理员或系统管理员
        if (roleIds.contains(RoleEnum.WB_ADMIN.getId()) || roleIds.contains(RoleEnum.WB_SYS.getId())) {
            int result = userDao.updatePassword(passwordEncoder(newPassword, user.getSalt()), userId);
            return ApiResult.ok();
        }

        // 机构管理员只能重置自己机构用户密码
        if (roleIds.contains(RoleEnum.ORG_ADMIN.getId())) {
            OrganizationDO orgDO = organizationDAO.selectByPrimaryKey(user.getOrganizationId());
            // TODO: 后续需要支持多级机构
            if (Objects.isNull(orgDO) || !orgDO.getId().equals(loginUser.getOrganizationId())) {
                log.info("reset password fail : {} - {}", loginUser.getId(), userId);
                return ApiResult.failOfMessage("无操作权限");
            }
            int result = userDao.updatePassword(passwordEncoder(newPassword, user.getSalt()), userId);
            return ApiResult.ok();
        }

        log.info("reset password fail : {} - {}", loginUser.getId(), userId);
        return ApiResult.failOfMessage("无操作权限");
    }

    @Override
    public ApiResult<Void> registerUser(UserParam user) {
        String verifyCodeKey = CacheKeyUtils.getVerifyCodeKey(user.getPhone());
        String localVerifyCode = redisTemplate.opsForValue().get(verifyCodeKey);
        if (StringUtils.isEmpty(user.getVerifyCode()) || !user.getVerifyCode().equals(localVerifyCode)) {
            return ApiResult.failOfMessage("verifyCode " + user.getVerifyCode() + "error");
        }
        UserDO newUserDO = ConvertUtil.convert(user, UserDO.class);
        newUserDO.setSalt(DigestUtils.md5Hex(UUID.randomUUID().toString() + System.currentTimeMillis() + UUID.randomUUID().toString()));
        newUserDO.setPassword(passwordEncoder(newUserDO.getPassword(), newUserDO.getSalt()));
        UserDO userByName = userDao.getUserByUsername(user.getUsername());
        UserDO userByMobilePhone = userDao.getUserByMobilePhone(user.getPhone());
        if (userByName != null || userByMobilePhone != null) {
            return ApiResult.failOfMessage("username or phone exists");
        }
        int result = userDao.addUser(newUserDO);
        if (result == 1) {
            return ApiResult.ok();
        }
        return ApiResult.failOfMessage("server error, try again");
    }

    @Override
    public ApiResult<Void> updateUserName(long userId, String userName) {
        int result = userDao.updateUsername(userName, userId);
        if (result != 1) {
            return ApiResult.failOfMessage("username exists");
        }
        return ApiResult.ok();
    }

    @Override
    public ApiResult<Void> importUser(UserDTO loginUser, List<UserImportDTO> userImportDTOs, List<String> errorList) {
        if (null == loginUser || null == loginUser.getRoleId() || 3L != loginUser.getRoleId()) {
            return ApiResult.failOfMessage("无操作权限");
        }

        if (CollectionUtils.isEmpty(userImportDTOs)) {
            return ApiResult.ok();
        }

        Long orgId = loginUser.getOrganizationId();
        String orgName = loginUser.getOrganizationName();

        for (UserImportDTO userImportDTO : userImportDTOs) {
            UserDO data = userDao.getUserByUsernameAndOrgId(userImportDTO.getUsername(), orgId);
            if (Objects.nonNull(data)) {
                errorList.add(String.format("第%d行解析异常:用户名 %s 已存在", userImportDTO.getRowIndex(), data.getUsername()));
            }

            data = userDao.getUserByPhoneAndOrgId(userImportDTO.getPhone(), orgId);
            if (Objects.nonNull(data)) {
                errorList.add(String.format("第%d行解析异常:手机号 %s 已存在", userImportDTO.getRowIndex(), data.getPhone()));
            }

            UserDO userDO = new UserDO();
            userDO.setUsername(userImportDTO.getUsername());
            userDO.setRealname(userImportDTO.getRealname());
            userDO.setGender(userImportDTO.getGender());
            userDO.setPhone(userImportDTO.getPhone());
            userDO.setEmail(userImportDTO.getEmail());
            userDO.setCertificateType(CertificateTypeEnum.IDENTITY_CARD.getType());
            userDO.setCertificateNo(userImportDTO.getCertificateNo());
            userDO.setDescription(userImportDTO.getDescription());
            userDO.setRoleId(userImportDTO.getRoleId());
            userDO.setOrganizationId(orgId);
            userDO.setOrganizationName(orgName);
            userDO.setIsDel(Boolean.FALSE);
            userDO.setSalt(DigestUtils.md5Hex(UUID.randomUUID().toString() + System.currentTimeMillis() + UUID.randomUUID().toString()));
            userDO.setPassword(passwordEncoder(SystemConstant.DEFAULT_USER_PASSWORD, userDO.getSalt()));
            userDO.setStatus(UserStatusEnum.ACTIVE.getValue());
            userDO.setCreatorId(loginUser.getId());
            userDO.setCreateGmt(new Date());
            userDO.setCreateType(UserCreateTypeEnum.IMPORT_BATCH.getType());
            userDO.setAppCode(SystemConstant.SYSTEM_CODE);
            userDao.addUser(userDO);

            Long userId = userDO.getId();

            UserRoleDO userRoleDO = new UserRoleDO();
            userRoleDO.setUserId(userId);
            userRoleDO.setRoleId(userImportDTO.getRoleId());
            userRoleDAO.insertSelective(userRoleDO);

            OrganizationUserDO organizationUserDO = new OrganizationUserDO();
            organizationUserDO.setUserId(userId);
            organizationUserDO.setOrganizationId(orgId);
            organizationUserDAO.insertSelective(organizationUserDO);
        }

        log.info("excel批量导入用户完成，返回结果如下：" + JSON.toJSONString(errorList));
        return ApiResult.ok();
    }
}
